package com.lds.sign_sdk.interceptor;

import com.lds.sign_sdk.annotation.FieldDesensitize;
import com.lds.sign_sdk.desensitizer.IDesensitizer;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.lang.invoke.MethodHandles;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.time.chrono.ChronoLocalDate;
import java.util.*;

/**
 * @Date 2022/7/29
 * @Time 19:21
 * @Author liudongs
 * @E-mail liudongs@aliyun.com
 * @Version 1.0.0
 * @Description
 **/
@Intercepts({@Signature(
        type = Executor.class,
        method = "query",
        args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}
), @Signature(
        type = Executor.class,
        method = "query",
        args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}
)})
@Component
public class DesensitizeInterceptor implements Interceptor {
    private static final Logger log = LoggerFactory.getLogger(DesensitizeInterceptor.class.getName());

    public DesensitizeInterceptor() {
    }

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        return this.selectHandle(invocation);
    }

    private Object selectHandle(Invocation invocation) throws Throwable {
        Object[] args = invocation.getArgs();
        MappedStatement ms = (MappedStatement)args[0];
        Object parameter = args[1];
        RowBounds rowBounds = (RowBounds)args[2];
        ResultHandler<Object> resultHandler = (ResultHandler)args[3];
        Executor executor = (Executor)invocation.getTarget();
        CacheKey cacheKey;
        BoundSql boundSql;
        if (args.length == 4) {
            boundSql = ms.getBoundSql(parameter);
            cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql);
        } else {
            cacheKey = (CacheKey)args[4];
            boundSql = (BoundSql)args[5];
        }

        List<Object> resultList = executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);

        HashMap fieldObjectHashMap = new HashMap<>();
        HashMap finalFieldObjectHashMap = fieldObjectHashMap;
        for(Iterator var11 = resultList.iterator(); var11.hasNext(); fieldObjectHashMap.keySet().forEach(key -> {
            try {
                this.handleString((Field) key, finalFieldObjectHashMap.get(key));
            } catch (InstantiationException | IllegalAccessException var4) {
                var4.printStackTrace();
            }

        })) {
            Object object = var11.next();
            fieldObjectHashMap = new HashMap();
            if (object != null) {
                this.handleObject(object, object.getClass(), fieldObjectHashMap);
            }
        }

        return resultList;
    }

    private boolean isFilter(Object object) {
        return object == null || object instanceof CharSequence || object instanceof Number || object instanceof Collection || object instanceof Date || object instanceof ChronoLocalDate;
    }

    private List<Field> mergeField(Class<?> oClass, List<Field> fields) {
        if (fields == null) {
            fields = new ArrayList();
        }

        Class<?> superclass = oClass.getSuperclass();
        if (superclass != null && !superclass.equals(Object.class) && superclass.getDeclaredFields().length > 0) {
            this.mergeField(superclass, (List)fields);
        }

        Field[] var4 = oClass.getDeclaredFields();
        int var5 = var4.length;

        for(int var6 = 0; var6 < var5; ++var6) {
            Field declaredField = var4[var6];
            int modifiers = declaredField.getModifiers();
            if (!Modifier.isStatic(modifiers) && !Modifier.isFinal(modifiers) && !Modifier.isVolatile(modifiers) && !Modifier.isSynchronized(modifiers)) {
                ((List)fields).add(declaredField);
            }
        }

        return (List)fields;
    }

    private void handleObject(Object obj, Class<?> oClass, HashMap<Field, Object> fieldObjectHashMap) throws IllegalAccessException {
        if (!this.isFilter(obj)) {
            List<Field> fields = this.mergeField(oClass, (List)null);
            Iterator var5 = fields.iterator();

            while(true) {
                while(true) {
                    Field declaredField;
                    Object value;
                    do {
                        do {
                            do {
                                if (!var5.hasNext()) {
                                    return;
                                }

                                declaredField = (Field)var5.next();
                            } while(Modifier.isStatic(declaredField.getModifiers()));

                            boolean accessible = declaredField.isAccessible();
                            declaredField.setAccessible(true);
                            value = declaredField.get(obj);
                            declaredField.setAccessible(accessible);
                        } while(value == null);
                    } while(value instanceof Number);

                    if (value instanceof String) {
                        FieldDesensitize annotation = (FieldDesensitize)declaredField.getAnnotation(FieldDesensitize.class);
                        if (annotation != null) {
                            fieldObjectHashMap.put(declaredField, obj);
                        }
                    } else if (value instanceof Collection) {
                        Collection coll = (Collection)value;
                        Iterator var10 = coll.iterator();

                        while(var10.hasNext()) {
                            Object o = var10.next();
                            if (this.isFilter(o)) {
                                break;
                            }

                            this.handleObject(o, o.getClass(), fieldObjectHashMap);
                        }
                    } else {
                        this.handleObject(value, value.getClass(), fieldObjectHashMap);
                    }
                }
            }
        }
    }

    private void handleString(Field field, Object object) throws IllegalAccessException, InstantiationException {
        boolean accessible = field.isAccessible();
        field.setAccessible(true);
        Object value = field.get(object);
        FieldDesensitize annotation = (FieldDesensitize)field.getAnnotation(FieldDesensitize.class);
        if (annotation != null) {
            String fillValue = annotation.fillValue();
            Class<? extends IDesensitizer> desensitizer = annotation.desensitizer();
            IDesensitizer iDesensitizer = (IDesensitizer)desensitizer.newInstance();
            String desensitizerValue = iDesensitizer.execute(String.valueOf(value), fillValue);
            log.debug("原值：" + value);
            log.debug("脱敏后：" + desensitizerValue);
            field.set(object, String.valueOf(desensitizerValue));
            field.setAccessible(accessible);
        }

    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
    }
}

